/* The file is saved in UTF-8 codepage.
 * Check: «Stereotype», Section mark-§, Copyright-©, Alpha-α, Beta-β, Smile-☺
 */
package cz.pecinovsky.english.lootp.manager;


import cz.pecinovsky.english.lootp.manager.CanvasManager_Envelope.CanvasManager;



////////////////////////////////////////////////////////////////////////////////
//%%+  +++++ End of ignored starting test - place for imports +++++

import cz.pecinovsky.english.lootp.utility.IO;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

//%X+ xxxxx Start of the ovejumped text with declaration of the envelope xxxxx
/*******************************************************************************
 *<pre>
 * Předchozí:   Není - toto_je_nově_definovaná_třída
 *              Ttt v projektu Ppp
 * Následující: Prozatím není
 *
 * Projekt  Ppp
 *   + Přidáno
 *   - Odebráno
 *   ~ Změněno
 *</pre>
 */

////////////////////////////////////////////////////////////////////////////////
//%X- ----- End of the ovejumped text with declaration of the envelope -----


/*******************************************************************************
 * Instance třídy {@code Controller} zprostředkovádá ovládání z klávesnice
 * objektu implementujícího rozhrnaí {@code IControlled}.
 *
 * @author Rudolf PECINOVSKÝ
 * @version 6.03.2017 — 2010-11-08
 */
public class Controller extends KeyAdapter
{
//== CONSTANT CLASS ATTRIBUTES =================================================

    private static final CanvasManager CM = CanvasManager.getInstance();

    private static final int[] IMPLICITNÍ_KLÁVESY =
                               {  KeyEvent.VK_LEFT,  KeyEvent.VK_RIGHT,
                                  KeyEvent.VK_UP,    KeyEvent.VK_DOWN,
                                  KeyEvent.VK_SPACE, KeyEvent.VK_ENTER,
                                  KeyEvent.VK_ESCAPE };

    private static final int VLEVO =0, VPRAVO=1, VZHŮRU=2, DOLŮ=3,
                             MEZERA=4, ENTER=5,  KONEC=6,  POČET=KONEC+1;

    private static final String[] NÁZVY =
           { "VLEVO", "VPRAVO", "VZHŮRU", "DOLŮ", "MEZERA", "ENTER", "KONEC" };



//== VARIABLE CLASS ATTRIBUTES =================================================
//== STATIC CONSTRUCTOR (CLASS INITIALIZER, STATIC INICITALIZING BLOCK) ========

    static {
        if ((IMPLICITNÍ_KLÁVESY.length != NÁZVY.length)  ||
            (IMPLICITNÍ_KLÁVESY.length != POČET))
        {
            throw new RuntimeException(
                    "\nNesouhlasí počet požadovaných kláves a jejich názvů");
        }
    }



//== CONSTANT INSTANCE ATTRIBUTES ==============================================

    private final IControlled ovládaný;

    private final int[] klávesy;

    private final KeyListener posluchač;



//== VARIABLE INSTANCE ATTRIBUTES ==============================================
//== CLASS GETTERS AND SETTERS =================================================

    /***************************************************************************
     * Vrátí pole s implicitními klávesami uvedenými v getIndexOf
     * vlevo, vpravo, vzhůru, dolů, mezera, enter, konec.
     *
     * @return Pole s implicitními klávesami
     */
    public static int[] getImplicitníKlávesy()
    {
        return IMPLICITNÍ_KLÁVESY.clone();
    }



//== OTHER NON-PRIVATE CLASS METHODS ===========================================

    /***************************************************************************
     * Zjistí od uživateli, kterými klávesami by chtěl ovládat zadané funkce,
     * a vrátí pole s kódy zadaných kláves.
     *
     * @param názvy Názvy jednotlivých funkcí
     * @return Pole s kódy zadaných kláíves
     */
    public static int[] zjistiKlávesy(String[] názvy)
    {
        final String[] výzvy = očíslujNázvy(názvy, true);
        oknoSVýzvou(výzvy);
        final Thread čekačka = new Thread("Čekačka") {
            @Override
            public void run()
            {
                try {
                    sleep(123456);
                }catch(InterruptedException ie) {
                    return;
                }
                System.out.println("Nedočkal jsem se - končím");
            }
        };
        final int   počet  = názvy.length;
        final int[] zadané = new int[počet];
        final int[] pořadí = new int[1];
        KeyListener kl = new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e)
            {
                int další = ++pořadí[0];
                int kc = e.getKeyCode();
                zadané[další-1] = kc;

                if (další == počet) {
                    System.out.println(KeyEvent.getKeyText(kc)+" - kód: " + kc);
                    CM.removeKeyboardListener(this);
                    čekačka.interrupt();
                }
                else {
                    System.out.print(KeyEvent.getKeyText(kc) +
                                     " - kód: " + kc + výzvy[další]);
                }
            }
        };
        String text = "\nZačínáme:" + výzvy[0];
        System.out.print(text);
        čekačka.start();
        CM.addKeyboardListener(kl);
        try {
            čekačka.join();
        } catch (InterruptedException ex) {
            //Nerní co řešit - když mne přeruší, tak končím
        }
        if (pořadí[0] == počet) {
            return zadané;
        }
        return null;
    }



//##############################################################################
//== CONSTUCTORS AND FACTORY METHODS ===========================================

    /***************************************************************************
     * Požádá uživatele o zadání kláves a pak vytvoří řadič,
     * který bude reagovat na stisky těchto kláves
     * a na jejich základě ovládat zadaný objekt.
     *
     * @param ovládaný Objekt ovládaný z klávesnice prostřednictvím řadiče
     * @return Požadovaný řadič
     */
    public static Controller vytvořPro(IControlled ovládaný)
    {
        int[] zadané = zjistiKlávesy(NÁZVY);
        Controller řadič  = new Controller(ovládaný, zadané, false);
        return řadič;
    }


    /***************************************************************************
     * Vytvoří nový řadič ovládaného objektu s implicitním nastavením kláves
     * a ihned jej přihlásí jako posluchače klávesnice.
     *
     * @param ovládaný Ovládaný objekt
     */
    public Controller(IControlled ovládaný)
    {
        this(ovládaný, IMPLICITNÍ_KLÁVESY, true);
    }


    /***************************************************************************
     * Vytvoří nový řadič ovládaného objektu s implicitním nastavením kláves.
     *
     * @param ovládaný  Ovládaný objekt
     * @param přihlásit Zda se má vytvořený řadič ihned přihlásit jako posluchač
     */
    public Controller(IControlled ovládaný, boolean přihlásit)
    {
        this(ovládaný, IMPLICITNÍ_KLÁVESY, true);
    }


    /***************************************************************************
     * Vytvoří nový řadič ovládaného objektu se zadaným nastavením kláves.
     *
     * @param ovládaný  Ovládaný objekt
     * @param klávesy   Pole kódy kláves, na něž se má reagovat
     * @param přihlásit Zda se má vytvořený řadič ihned přihlásit jako posluchač
     */
    public Controller(IControlled ovládaný, int[] klávesy, boolean přihlásit)
    {
        if (klávesy.length != IMPLICITNÍ_KLÁVESY.length) {
            throw new RuntimeException(
                    "\nŠpatný počet ovládacích kláves -\nŘadič je připraven " +
                    "ovládat svěřené objekty prostřednictvím právě " +
                    IMPLICITNÍ_KLÁVESY.length + " kláves");

        }
        this.ovládaný = ovládaný;
        this.klávesy  = klávesy;
        this.posluchač = new PosluchačIf();
        CM.add(ovládaný);
        if (přihlásit) {
            start();
        }
    }



//== ABSTRACT METHODS ==========================================================
//== INSTANCE GETTERS AND SETTERS ==============================================

    /***************************************************************************
     * Vrátí odkaz na ovládaný objekt.
     *
     * @return Odkaz na ovládaný objekt
     */
    public IControlled getOvládaný()
    {
        return ovládaný;
    }



//== OTHER NON-PRIVATE INSTANCE METHODS ========================================

    /***************************************************************************
     * Odhlásí se jako posluchač klávesnice.
     * Pro opětovné přihlášení slouží metoda {@link #start()}.
     */
    public void konec()
    {
        CM.removeKeyboardListener(posluchač);
    }


    /***************************************************************************
     * Přihlhlásí se jako posluchač klávesnice.
     * Po tomto odhlášení se již nemůže opět přihlásit.
     */
    public final void start()
    {
        CM.addToFront(ovládaný);
        CM.addKeyboardListener(posluchač);
    }


    /***************************************************************************
     * Vrací textovou reprezentaci (podpis) dané instance
     * používanou především k ladicím účelům.
     *
     * @return Požadovaná textová reprezentace
     */
    @Override
    public String toString()
    {
        return "Řadič_(ovládaný=" + ovládaný + ")";
    }



//== PRIVATE AND AUXILIARY CLASS METHODS =======================================

    /***************************************************************************
     * Zobrazí okno s vypsanými popisky jednotlivých zadávaných kláves.
     *
     * @param texty Testy vypisované v dialogovém okně
     */
    private static void oknoSVýzvou(String[] texty)
    {
        int počet = texty.length;
        StringBuilder sb = new StringBuilder(
                                "Stiskem OK otvrďte přečtení této zprávy" +
                              "\na pak postupně stiskněte ");
        sb.append(počet).append(" kláves\ns významy v následujícím pořadí\n");
        for (int i=0;   i < počet;   i++) {
            sb.append(texty[i]);
        }
        sb.append("\n\nStandardní výstup vás bude navigovat");
        IO.inform(sb.toString());
    }

    /***************************************************************************
     * Vráti pole, v němž budou očíslované texty ze zadaného pole.
     * Texty ve vráceném poli budou číslovány od jedničky.
     *
     * @param názvy    Texty, které je třeba očíslovat
     * @param newLine  Má-li se na začátek očíslovaných textů vkládat new line
     * @return Pole s očíslovanými texty
     */
    private static String[] očíslujNázvy(String[] názvy, boolean newLine)
    {
        int počet = názvy.length;
        String nl = newLine  ?  "\n"  :  "";
        String[] očíslované  = new String[počet];
        for (int i=0;   i < počet;   i++) {
            očíslované[i] = nl + (i+1) + ". " + názvy[i] + ": ";
        }
        return očíslované;
    }



//== PRIVATE AND AUXILIARY INSTANCE METHODS ====================================
//== EMBEDDED TYPES AND INNER CLASSES ==========================================

    /***************************************************************************
     * Instance třídy PosluchačCase slouží jako posluchač klávesnice
     * řídící pohyb UFO v druhé variantě hry.
     */
    private
    class PosluchačCase extends KeyAdapter
    {
        /***********************************************************************
         * Ošetření klávesnice.
         */
        @Override
        public void keyPressed(KeyEvent e)
        {
            int kc = e.getKeyCode();
            switch(kc)
            {
                case KeyEvent.VK_LEFT:
                    ovládaný.vlevo();
                    return;

                case KeyEvent.VK_RIGHT:
                    ovládaný.vpravo();
                    return;

                case KeyEvent.VK_UP:
                    ovládaný.vzhůru();
                    return;

                case KeyEvent.VK_DOWN:
                    ovládaný.dolů();
                    return;

                case KeyEvent.VK_SPACE:
                    ovládaný.mezera();
                    return;

                case KeyEvent.VK_ENTER:
                    ovládaný.enter();
                    return;

                case KeyEvent.VK_ESCAPE:
                    ovládaný.konec();
                    Controller.this.konec();
                    return;
            }
        }

    }



    /***************************************************************************
     * Instance třídy PosluchačCase slouží jako posluchač klávesnice
     * řídící pohyb UFO v druhé variantě hry.
     */
    private
    class PosluchačIf extends KeyAdapter
    {
        /***********************************************************************
         * Ošetření klávesnice.
         */
        @Override
        public void keyPressed(KeyEvent e)
        {
            int kc = e.getKeyCode();
            if (kc == klávesy[VLEVO]) {
                ovládaný.vlevo();
            }
            else if (kc == klávesy[VPRAVO]) {
                ovládaný.vpravo();
            }
            else if (kc == klávesy[VZHŮRU]) {
                ovládaný.vzhůru();
            }
            else if (kc == klávesy[DOLŮ]) {
                ovládaný.dolů();
            }
            else if (kc == klávesy[MEZERA]) {
                ovládaný.mezera();
            }
            else if (kc == klávesy[ENTER]) {
                ovládaný.enter();
            }
            else if (kc == klávesy[KONEC]) {
                ovládaný.konec();
                Controller.this.konec();
            }
        }
    }



//== TESTING CLASSES AND METHODS ===============================================
//%I+
//}
//%I-
//%%- ---- Start of the ovejumped text with closing of the envelope ----
////////////////////////////////////////////////////////////////////////////////
//
//    /***************************************************************************
//     *
//     */
//    public static void test() {
//        Controller r = vytvořPro((IControlled)null);
//    }
//    /** @param args Parametry příkazového řádku */
//    public static void main(String... args) { test(); }
}
